#include <pty.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

//----------------------------------------------------------------------------//
//                                                                            //
//----------------------------------------------------------------------------//
static int pts_name(int fd, char **pts, size_t buf_len)
{
 int rv;
 char *buf = *pts;
 
 for(;;)
 {
  char *new_buf;
  
  if(buf_len)
  {
   rv = ptsname_r(fd, buf, buf_len);
   
   if(rv != 0 || memchr(buf, '\0', buf_len))
   /* We either got an error, or we succeeded and the
    returned name fit in the buffer */
    break;
    
    /* Try again with a longer buffer */
    buf_len += buf_len;  /* Double it */           
  }
  else
      /* No initial buffer; start out by mallocing one */
      buf_len = 128;        /* First time guess */
  
  if(buf != *pts)
  /* We've already malloced another buffer at least once */
     new_buf = (char *)realloc(buf, buf_len);
  else
      new_buf = (char *)malloc(buf_len);
  if(!new_buf)
  {
   rv = -1;
   errno = ENOMEM;
   break;            
  }
  buf = new_buf;
 }
 
 if(rv == 0)
      *pts = buf;   /* Return buffer to the user */
 else if(buf != *pts)
      free(buf);    /* Free what we malloced when returning an error. */
      
 return rv;
}

//----------------------------------------------------------------------------//
//                                                                            //
//----------------------------------------------------------------------------//
int login_tty(int fd)
{
 (void) setsid();
 #ifdef TIOCSCTTY
        if(ioctl(fd, TIOCSCTTY, (char *)NULL) == -1)
        return (-1);
 #else
      {
       /* This might work */
       char *fdname = ttyname(fd);
       int newfd;
       if(fdname)
       {
        if(fd != 0)
              (void) close(0);
        if(fd != 1) 
              (void) close(1);
        if(fd != 2)
              (void) close(2);
        newfd = open(fdname, O_RDWR);
        (void) close(newfd);         
       }
      }
 #endif
       (void) dup2(fd, 0);
       (void) dup2(fd, 1);
       (void) dup2(fd, 2);
       
       if(fd > 2)
             (void) close(fd);
       return (0);
}

//----------------------------------------------------------------------------//
//                                                                            //
//----------------------------------------------------------------------------//
int openpty( int *amaster,
             int *aslave,
             char *name,
             struct termios *termp,
             struct winsize *winp )
{
#ifdef PATH_MAX
       char _buf[PATH_MAX];
#else
       char _buf[512];
#endif 
       char *buf = _buf;
       int master, slave;
       
       master = getpt();
       if(master == -1)
       {
       printf("cannot open master");
       return -1;
      }
       
       if(grantpt(master))
       goto fail;
       
       if(unlockpt(master))
       goto fail;
       
       if(pts_name(master, &buf, sizeof(_buf)))
       goto fail;
       
       printf("%s\n", buf);
       slave = open(buf, O_RDWR | O_NOCTTY);
       if(slave == -1)
       {
        if(buf != _buf)
        free(buf);
        
        goto fail;         
       }   
       
       /* XXX Should we ignore errors here? */
       if(termp)
       tcsetattr(slave, TCSAFLUSH, termp);
       if(winp)
       ioctl(slave, TIOCSWINSZ, winp);
       
       *amaster = master;
       *aslave = slave;
       if(name != NULL)
       strcpy(name, buf);
       
       if(buf != _buf)
              free(buf);
       return 0;
       
       fail:
            printf("fail");
            close(master);
            return -1;
}

//----------------------------------------------------------------------------//
//                                                                            //
//----------------------------------------------------------------------------//
int forkpty( int *amaster,
             char *name,
             struct termios *termp,
             struct winsize *winp )
{

    int master, slave, pid;
    if(openpty(&master, &slave, name, termp, winp) == -1)
    return -1;
    
    switch(pid = fork())
    {
     case -1:
          return -1;
     case 0:
          /* Child */
          close(master);
          if(login_tty(slave))
          _exit(1);
          
          return 0;
     default:
             /* Parent */
             *amaster = master;
             close(slave);
             
             return pid;
    }
}

